<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML
><HEAD
><TITLE
>Manipulating Strings</TITLE
><META
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.7"><LINK
REL="HOME"
TITLE="Advanced Bash-Scripting Guide"
HREF="index.html"><LINK
REL="UP"
TITLE="Manipulating Variables"
HREF="manipulatingvars.html"><LINK
REL="PREVIOUS"
TITLE="Manipulating Variables"
HREF="manipulatingvars.html"><LINK
REL="NEXT"
TITLE="Parameter Substitution"
HREF="parameter-substitution.html"></HEAD
><BODY
CLASS="SECT1"
BGCOLOR="#FFFFFF"
TEXT="#000000"
LINK="#0000FF"
VLINK="#840084"
ALINK="#0000FF"
><DIV
CLASS="NAVHEADER"
><TABLE
SUMMARY="Header navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TH
COLSPAN="3"
ALIGN="center"
>Advanced Bash-Scripting Guide: </TH
></TR
><TR
><TD
WIDTH="10%"
ALIGN="left"
VALIGN="bottom"
><A
HREF="manipulatingvars.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="80%"
ALIGN="center"
VALIGN="bottom"
>Chapter 10. Manipulating Variables</TD
><TD
WIDTH="10%"
ALIGN="right"
VALIGN="bottom"
><A
HREF="parameter-substitution.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
></TABLE
><HR
ALIGN="LEFT"
WIDTH="100%"></DIV
><DIV
CLASS="SECT1"
><H1
CLASS="SECT1"
><A
NAME="STRING-MANIPULATION"
></A
>10.1. Manipulating Strings</H1
><P
><A
NAME="STRINGMANIP"
></A
></P
><P
>Bash supports a surprising number of string manipulation
	      operations.  Unfortunately, these tools lack
	      a unified focus. Some are a subset of <A
HREF="parameter-substitution.html#PARAMSUBREF"
>parameter substitution</A
>, and
	      others fall under the functionality of the UNIX <A
HREF="moreadv.html#EXPRREF"
>expr</A
> command. This results in
	      inconsistent command syntax and overlap of functionality,
	      not to mention confusion.</P
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
>String Length</B
></P
><DL
><DT
>${#string}</DT
><DD
><P
></P
></DD
><DT
>expr length $string</DT
><DD
><P
><A
NAME="STRLEN"
></A
>These are the equivalent of
            <I
CLASS="FIRSTTERM"
>strlen()</I
> in
            <I
CLASS="FIRSTTERM"
>C</I
>.</P
></DD
><DT
>expr "$string" : '.*'</DT
><DD
><P
>	  <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>stringZ=abcABC123ABCabc

echo ${#stringZ}                 # 15
echo `expr length $stringZ`      # 15
echo `expr "$stringZ" : '.*'`    # 15</PRE
></FONT
></TD
></TR
></TABLE
>
	  </P
></DD
></DL
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="PARAGRAPHSPACE"
></A
><P
><B
>Example 10-1. Inserting a blank line between paragraphs in a text file</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# paragraph-space.sh
# Ver. 2.1, Reldate 29Jul12 [fixup]

# Inserts a blank line between paragraphs of a single-spaced text file.
# Usage: $0 &#60;FILENAME

MINLEN=60        # Change this value? It's a judgment call.
#  Assume lines shorter than $MINLEN characters ending in a period
#+ terminate a paragraph. See exercises below.

while read line  # For as many lines as the input file has ...
do
  echo "$line"   # Output the line itself.

  len=${#line}
  if [[ "$len" -lt "$MINLEN" &#38;&#38; "$line" =~ [*{\.}]$ ]]
# if [[ "$len" -lt "$MINLEN" &#38;&#38; "$line" =~ \[*\.\] ]]
# An update to Bash broke the previous version of this script. Ouch!
# Thank you, Halim Srama, for pointing this out and suggesting a fix.
    then echo    #  Add a blank line immediately
  fi             #+ after a short line terminated by a period.
done

exit

# Exercises:
# ---------
#  1) The script usually inserts a blank line at the end
#+    of the target file. Fix this.
#  2) Line 17 only considers periods as sentence terminators.
#     Modify this to include other common end-of-sentence characters,
#+    such as ?, !, and ".</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
>Length of Matching Substring at Beginning of String</B
></P
><DL
><DT
><A
NAME="EXPRMATCH"
></A
>expr match "$string"
	  '$substring'</DT
><DD
><P
><TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> is a <A
HREF="regexp.html#REGEXREF"
>regular expression</A
>.</P
></DD
><DT
>expr "$string" : '$substring'</DT
><DD
><P
><TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> is a regular
	    expression.</P
><P
>&#13;	  <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>stringZ=abcABC123ABCabc
#       |------|
#       12345678

echo `expr match "$stringZ" 'abc[A-Z]*.2'`   # 8
echo `expr "$stringZ" : 'abc[A-Z]*.2'`       # 8</PRE
></FONT
></TD
></TR
></TABLE
>
          </P
></DD
></DL
></DIV
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
>Index</B
></P
><DL
><DT
><A
NAME="SUBSTRINGINDEX2"
></A
>expr index $string
	    $substring</DT
><DD
><P
>Numerical position in $string of first character in 
	    $substring that matches.</P
><P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>stringZ=abcABC123ABCabc
#       123456 ...
echo `expr index "$stringZ" C12`             # 6
                                             # C position.

echo `expr index "$stringZ" 1c`              # 3
# 'c' (in #3 position) matches before '1'.</PRE
></FONT
></TD
></TR
></TABLE
></P
><P
>This is the near equivalent of
            <I
CLASS="FIRSTTERM"
>strchr()</I
> in
            <I
CLASS="FIRSTTERM"
>C</I
>.</P
></DD
></DL
></DIV
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
>Substring Extraction</B
></P
><DL
><DT
><A
NAME="SUBSTREXTR01"
></A
>${string:position}</DT
><DD
><P
>Extracts substring from <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
> at
	    <TT
CLASS="REPLACEABLE"
><I
>$position</I
></TT
>.</P
><P
>If the <TT
CLASS="VARNAME"
>$string</TT
> parameter is
	    <SPAN
CLASS="QUOTE"
>"<SPAN
CLASS="TOKEN"
>*</SPAN
>"</SPAN
>
	    or <SPAN
CLASS="QUOTE"
>"<SPAN
CLASS="TOKEN"
>@</SPAN
>"</SPAN
>, then this extracts the
	    <A
HREF="internalvariables.html#POSPARAMREF"
>positional parameters</A
>,
	       <A
NAME="AEN5987"
HREF="#FTN.AEN5987"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
>
	    starting at <TT
CLASS="VARNAME"
>$position</TT
>.</P
></DD
><DT
><A
NAME="SUBSTREXTR02"
></A
>${string:position:length}</DT
><DD
><P
>Extracts <TT
CLASS="REPLACEABLE"
><I
>$length</I
></TT
> characters
	    of substring from <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
> at
	    <TT
CLASS="REPLACEABLE"
><I
>$position</I
></TT
>.</P
><P
>	  <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>stringZ=abcABC123ABCabc
#       0123456789.....
#       0-based indexing.

echo ${stringZ:0}                            # abcABC123ABCabc
echo ${stringZ:1}                            # bcABC123ABCabc
echo ${stringZ:7}                            # 23ABCabc

echo ${stringZ:7:3}                          # 23A
                                             # Three characters of substring.



# Is it possible to index from the right end of the string?
    
echo ${stringZ:-4}                           # abcABC123ABCabc
# Defaults to full string, as in ${parameter:-default}.
# However . . .

echo ${stringZ:(-4)}                         # Cabc 
echo ${stringZ: -4}                          # Cabc
# Now, it works.
# Parentheses or added space "escape" the position parameter.

# Thank you, Dan Jacobson, for pointing this out.</PRE
></FONT
></TD
></TR
></TABLE
>
	  </P
><P
>The <I
CLASS="FIRSTTERM"
>position</I
> and
             <I
CLASS="FIRSTTERM"
>length</I
> arguments can be
	     <SPAN
CLASS="QUOTE"
>"parameterized,"</SPAN
> that is, represented as a
	     variable, rather than as a numerical constant.</P
><P
><A
NAME="RANDSTRING0"
></A
></P
><DIV
CLASS="EXAMPLE"
><A
NAME="RANDSTRING"
></A
><P
><B
>Example 10-2. Generating an 8-character <SPAN
CLASS="QUOTE"
>"random"</SPAN
>
	        string</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# rand-string.sh
# Generating an 8-character "random" string.

if [ -n "$1" ]  #  If command-line argument present,
then            #+ then set start-string to it.
  str0="$1"
else            #  Else use PID of script as start-string.
  str0="$$"
fi

POS=2  # Starting from position 2 in the string.
LEN=8  # Extract eight characters.

str1=$( echo "$str0" | md5sum | md5sum )
#  Doubly scramble     ^^^^^^   ^^^^^^
#+ by piping and repiping to md5sum.

randstring="${str1:$POS:$LEN}"
# Can parameterize ^^^^ ^^^^

echo "$randstring"

exit $?

# bozo$ ./rand-string.sh my-password
# 1bdd88c4

#  No, this is is not recommended
#+ as a method of generating hack-proof passwords.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
><A
NAME="SUBSTREXTRP"
></A
></P
><P
>If the <TT
CLASS="VARNAME"
>$string</TT
> parameter is
	    <SPAN
CLASS="QUOTE"
>"<SPAN
CLASS="TOKEN"
>*</SPAN
>"</SPAN
> or
	    <SPAN
CLASS="QUOTE"
>"<SPAN
CLASS="TOKEN"
>@</SPAN
>"</SPAN
>, then this extracts a maximum
	    of <TT
CLASS="VARNAME"
>$length</TT
> positional parameters, starting
	    at <TT
CLASS="VARNAME"
>$position</TT
>.</P
><P
>	  <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>echo ${*:2}          # Echoes second and following positional parameters.
echo ${@:2}          # Same as above.

echo ${*:2:3}        # Echoes three positional parameters, starting at second.</PRE
></FONT
></TD
></TR
></TABLE
>
	  </P
></DD
><DT
>expr substr $string $position $length</DT
><DD
><P
>Extracts <TT
CLASS="REPLACEABLE"
><I
>$length</I
></TT
> characters
	    from <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
> starting at
	    <TT
CLASS="REPLACEABLE"
><I
>$position</I
></TT
>.</P
><P
>	  <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>stringZ=abcABC123ABCabc
#       123456789......
#       1-based indexing.

echo `expr substr $stringZ 1 2`              # ab
echo `expr substr $stringZ 4 3`              # ABC</PRE
></FONT
></TD
></TR
></TABLE
>
          </P
><P
><A
NAME="EXPRPAREN"
></A
></P
></DD
><DT
>expr match "$string" '\($substring\)'</DT
><DD
><P
>Extracts <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
>
	    at beginning of <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
>,
	    where <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> is a <A
HREF="regexp.html#REGEXREF"
>regular expression</A
>.</P
></DD
><DT
>expr "$string" : '\($substring\)'</DT
><DD
><P
>Extracts <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
>
	    at beginning of <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
>,
	    where <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> is a regular
	    expression.</P
><P
>	    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>stringZ=abcABC123ABCabc
#       =======	    

echo `expr match "$stringZ" '\(.[b-c]*[A-Z]..[0-9]\)'`   # abcABC1
echo `expr "$stringZ" : '\(.[b-c]*[A-Z]..[0-9]\)'`       # abcABC1
echo `expr "$stringZ" : '\(.......\)'`                   # abcABC1
# All of the above forms give an identical result.</PRE
></FONT
></TD
></TR
></TABLE
>
	    </P
></DD
><DT
>expr match "$string" '.*\($substring\)'</DT
><DD
><P
>Extracts <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
>
	    at <EM
>end</EM
> of
	    <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
>, where
	    <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> is a regular
	    expression.</P
></DD
><DT
>expr "$string" : '.*\($substring\)'</DT
><DD
><P
>Extracts <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
>
	    at <EM
>end</EM
> of <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
>,
	    where <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> is a regular
	    expression.</P
><P
>	    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>stringZ=abcABC123ABCabc
#                ======

echo `expr match "$stringZ" '.*\([A-C][A-C][A-C][a-c]*\)'`    # ABCabc
echo `expr "$stringZ" : '.*\(......\)'`                       # ABCabc</PRE
></FONT
></TD
></TR
></TABLE
>

	    </P
></DD
></DL
></DIV
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
>Substring Removal</B
></P
><DL
><DT
>${string#substring}</DT
><DD
><P
>Deletes shortest match of
	    <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> from
	    <EM
>front</EM
> of
	    <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
>.</P
></DD
><DT
>${string##substring}</DT
><DD
><P
>Deletes longest match of
	    <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> from
	    <EM
>front</EM
> of
	    <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
>.</P
><P
>  
	  <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>stringZ=abcABC123ABCabc
#       |----|          shortest
#       |----------|    longest

echo ${stringZ#a*C}      # 123ABCabc
# Strip out shortest match between 'a' and 'C'.

echo ${stringZ##a*C}     # abc
# Strip out longest match between 'a' and 'C'.



# You can parameterize the substrings.

X='a*C'

echo ${stringZ#$X}      # 123ABCabc
echo ${stringZ##$X}     # abc
                        # As above.</PRE
></FONT
></TD
></TR
></TABLE
>
	  </P
></DD
><DT
>${string%substring}</DT
><DD
><P
>Deletes shortest match of
	    <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> from
	    <EM
>back</EM
> of
	    <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
>.</P
><P
>For example:
	    <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
># Rename all filenames in $PWD with "TXT" suffix to a "txt" suffix.
# For example, "file1.TXT" becomes "file1.txt" . . .

SUFF=TXT
suff=txt

for i in $(ls *.$SUFF)
do
  mv -f $i ${i%.$SUFF}.$suff
  #  Leave unchanged everything *except* the shortest pattern match
  #+ starting from the right-hand-side of the variable $i . . .
done ### This could be condensed into a "one-liner" if desired.

# Thank you, Rory Winston.</PRE
></FONT
></TD
></TR
></TABLE
>
	  </P
></DD
><DT
>${string%%substring}</DT
><DD
><P
>Deletes longest match of
	    <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> from
	    <EM
>back</EM
> of
	    <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
>.</P
><P
>  
	  <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>stringZ=abcABC123ABCabc
#                    ||     shortest
#        |------------|     longest

echo ${stringZ%b*c}      # abcABC123ABCa
# Strip out shortest match between 'b' and 'c', from back of $stringZ.

echo ${stringZ%%b*c}     # a
# Strip out longest match between 'b' and 'c', from back of $stringZ.</PRE
></FONT
></TD
></TR
></TABLE
>
	  </P
><P
>This operator is useful for generating filenames.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="CVT"
></A
><P
><B
>Example 10-3. Converting graphic file formats, with filename change</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
#  cvt.sh:
#  Converts all the MacPaint image files in a directory to "pbm" format.

#  Uses the "macptopbm" binary from the "netpbm" package,
#+ which is maintained by Brian Henderson (bryanh@giraffe-data.com).
#  Netpbm is a standard part of most Linux distros.

OPERATION=macptopbm
SUFFIX=pbm          # New filename suffix. 

if [ -n "$1" ]
then
  directory=$1      # If directory name given as a script argument...
else
  directory=$PWD    # Otherwise use current working directory.
fi  
  
#  Assumes all files in the target directory are MacPaint image files,
#+ with a ".mac" filename suffix.

for file in $directory/*    # Filename globbing.
do
  filename=${file%.*c}      #  Strip ".mac" suffix off filename
                            #+ ('.*c' matches everything
			    #+ between '.' and 'c', inclusive).
  $OPERATION $file &#62; "$filename.$SUFFIX"
                            # Redirect conversion to new filename.
  rm -f $file               # Delete original files after converting.   
  echo "$filename.$SUFFIX"  # Log what is happening to stdout.
done

exit 0

# Exercise:
# --------
#  As it stands, this script converts *all* the files in the current
#+ working directory.
#  Modify it to work *only* on files with a ".mac" suffix.



# *** And here's another way to do it. *** #

#!/bin/bash
# Batch convert into different graphic formats.
# Assumes imagemagick installed (standard in most Linux distros).

INFMT=png   # Can be tif, jpg, gif, etc.
OUTFMT=pdf  # Can be tif, jpg, gif, pdf, etc.

for pic in *"$INFMT"
do
  p2=$(ls "$pic" | sed -e s/\.$INFMT//)
  # echo $p2
    convert "$pic" $p2.$OUTFMT
    done

exit $?</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><DIV
CLASS="EXAMPLE"
><A
NAME="RA2OGG"
></A
><P
><B
>Example 10-4. Converting streaming audio files to
	      <I
CLASS="FIRSTTERM"
>ogg</I
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# ra2ogg.sh: Convert streaming audio files (*.ra) to ogg.

# Uses the "mplayer" media player program:
#      http://www.mplayerhq.hu/homepage
# Uses the "ogg" library and "oggenc":
#      http://www.xiph.org/
#
# This script may need appropriate codecs installed, such as sipr.so ...
# Possibly also the compat-libstdc++ package.


OFILEPREF=${1%%ra}      # Strip off the "ra" suffix.
OFILESUFF=wav           # Suffix for wav file.
OUTFILE="$OFILEPREF""$OFILESUFF"
E_NOARGS=85

if [ -z "$1" ]          # Must specify a filename to convert.
then
  echo "Usage: `basename $0` [filename]"
  exit $E_NOARGS
fi


##########################################################################
mplayer "$1" -ao pcm:file=$OUTFILE
oggenc "$OUTFILE"  # Correct file extension automatically added by oggenc.
##########################################################################

rm "$OUTFILE"      # Delete intermediate *.wav file.
                   # If you want to keep it, comment out above line.

exit $?

#  Note:
#  ----
#  On a Website, simply clicking on a *.ram streaming audio file
#+ usually only downloads the URL of the actual *.ra audio file.
#  You can then use "wget" or something similar
#+ to download the *.ra file itself.


#  Exercises:
#  ---------
#  As is, this script converts only *.ra filenames.
#  Add flexibility by permitting use of *.ram and other filenames.
#
#  If you're really ambitious, expand the script
#+ to do automatic downloads and conversions of streaming audio files.
#  Given a URL, batch download streaming audio files (using "wget")
#+ and convert them on the fly.</PRE
></FONT
></TD
></TR
></TABLE
></DIV
><P
><A
NAME="GETOPTSIMPLE1"
></A
></P
><P
>A simple emulation of <A
HREF="extmisc.html#GETOPTY"
>getopt</A
>
	    using substring-extraction constructs.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="GETOPTSIMPLE"
></A
><P
><B
>Example 10-5. Emulating <I
CLASS="FIRSTTERM"
>getopt</I
></B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# getopt-simple.sh
# Author: Chris Morgan
# Used in the ABS Guide with permission.


getopt_simple()
{
    echo "getopt_simple()"
    echo "Parameters are '$*'"
    until [ -z "$1" ]
    do
      echo "Processing parameter of: '$1'"
      if [ ${1:0:1} = '/' ]
      then
          tmp=${1:1}               # Strip off leading '/' . . .
          parameter=${tmp%%=*}     # Extract name.
          value=${tmp##*=}         # Extract value.
          echo "Parameter: '$parameter', value: '$value'"
          eval $parameter=$value
      fi
      shift
    done
}

# Pass all options to getopt_simple().
getopt_simple $*

echo "test is '$test'"
echo "test2 is '$test2'"

exit 0  # See also, UseGetOpt.sh, a modified version of this script.

---

sh getopt_example.sh /test=value1 /test2=value2

Parameters are '/test=value1 /test2=value2'
Processing parameter of: '/test=value1'
Parameter: 'test', value: 'value1'
Processing parameter of: '/test2=value2'
Parameter: 'test2', value: 'value2'
test is 'value1'
test2 is 'value2'&#13;</PRE
></FONT
></TD
></TR
></TABLE
></DIV
></DD
></DL
></DIV
><P
></P
><DIV
CLASS="VARIABLELIST"
><P
><B
>Substring Replacement</B
></P
><DL
><DT
><A
NAME="SUBSTRREPL00"
></A
>${string/substring/replacement}</DT
><DD
><P
>	    Replace first <I
CLASS="FIRSTTERM"
>match</I
> of
	    <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> with
	    <TT
CLASS="REPLACEABLE"
><I
>$replacement</I
></TT
>.
	      <A
NAME="AEN6164"
HREF="#FTN.AEN6164"
><SPAN
CLASS="footnote"
>[2]</SPAN
></A
>
	    </P
></DD
><DT
><A
NAME="SUBSTRREPL01"
></A
>${string//substring/replacement}</DT
><DD
><P
>Replace all matches of
	    <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> with
	    <TT
CLASS="REPLACEABLE"
><I
>$replacement</I
></TT
>.</P
><P
>  
	  <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>stringZ=abcABC123ABCabc

echo ${stringZ/abc/xyz}       # xyzABC123ABCabc
                              # Replaces first match of 'abc' with 'xyz'.

echo ${stringZ//abc/xyz}      # xyzABC123ABCxyz
                              # Replaces all matches of 'abc' with # 'xyz'.

echo  ---------------
echo "$stringZ"               # abcABC123ABCabc
echo  ---------------
                              # The string itself is not altered!

# Can the match and replacement strings be parameterized?
match=abc
repl=000
echo ${stringZ/$match/$repl}  # 000ABC123ABCabc
#              ^      ^         ^^^
echo ${stringZ//$match/$repl} # 000ABC123ABC000
# Yes!          ^      ^        ^^^         ^^^

echo

# What happens if no $replacement string is supplied?
echo ${stringZ/abc}           # ABC123ABCabc
echo ${stringZ//abc}          # ABC123ABC
# A simple deletion takes place.</PRE
></FONT
></TD
></TR
></TABLE
>
	  </P
></DD
><DT
><A
NAME="SUBSTRREPL02"
></A
>${string/#substring/replacement}</DT
><DD
><P
>If <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> matches
	    <EM
>front</EM
> end of
	    <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
>, substitute
	    <TT
CLASS="REPLACEABLE"
><I
>$replacement</I
></TT
> for
	    <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
>.</P
></DD
><DT
><A
NAME="SUBSTRREPL03"
></A
>${string/%substring/replacement}</DT
><DD
><P
>If <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> matches
	    <EM
>back</EM
> end of
	    <TT
CLASS="REPLACEABLE"
><I
>$string</I
></TT
>, substitute
	    <TT
CLASS="REPLACEABLE"
><I
>$replacement</I
></TT
> for
	    <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
>.</P
><P
>  
          <TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="90%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>stringZ=abcABC123ABCabc

echo ${stringZ/#abc/XYZ}          # XYZABC123ABCabc
                                  # Replaces front-end match of 'abc' with 'XYZ'.

echo ${stringZ/%abc/XYZ}          # abcABC123ABCXYZ
                                  # Replaces back-end match of 'abc' with 'XYZ'.</PRE
></FONT
></TD
></TR
></TABLE
>
	  </P
></DD
></DL
></DIV
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="AWKSTRINGMANIP"
></A
>10.1.1. Manipulating strings using awk</H2
><P
><A
NAME="AWKSTRINGMANIP2"
></A
></P
><P
>A Bash script may invoke the string manipulation facilities of
	    <A
HREF="awk.html#AWKREF"
>awk</A
> as an alternative to using its
	    built-in operations.</P
><DIV
CLASS="EXAMPLE"
><A
NAME="SUBSTRINGEX"
></A
><P
><B
>Example 10-6. Alternate ways of extracting and locating substrings</B
></P
><TABLE
BORDER="0"
BGCOLOR="#E0E0E0"
WIDTH="100%"
><TR
><TD
><FONT
COLOR="#000000"
><PRE
CLASS="PROGRAMLISTING"
>#!/bin/bash
# substring-extraction.sh

String=23skidoo1
#      012345678    Bash
#      123456789    awk
# Note different string indexing system:
# Bash numbers first character of string as 0.
# Awk  numbers first character of string as 1.

echo ${String:2:4} # position 3 (0-1-2), 4 characters long
                                         # skid

# The awk equivalent of ${string:pos:length} is substr(string,pos,length).
echo | awk '
{ print substr("'"${String}"'",3,4)      # skid
}
'
#  Piping an empty "echo" to awk gives it dummy input,
#+ and thus makes it unnecessary to supply a filename.

echo "----"

# And likewise:

echo | awk '
{ print index("'"${String}"'", "skid")      # 3
}                                           # (skid starts at position 3)
'   # The awk equivalent of "expr index" ...

exit 0</PRE
></FONT
></TD
></TR
></TABLE
></DIV
></DIV
><DIV
CLASS="SECT2"
><H2
CLASS="SECT2"
><A
NAME="STRFDISC"
></A
>10.1.2. Further Reference</H2
><P
>For more on string manipulation in scripts, refer to <A
HREF="parameter-substitution.html"
>Section 10.2</A
> and the
       <A
HREF="moreadv.html#EXPEXTRSUB"
>relevant section</A
> of the <A
HREF="moreadv.html#EXPRREF"
>expr</A
> command listing.</P
><P
>Script examples:
      <P
></P
><OL
TYPE="1"
><LI
><P
><A
HREF="moreadv.html#EX45"
>Example 16-9</A
></P
></LI
><LI
><P
><A
HREF="parameter-substitution.html#LENGTH"
>Example 10-9</A
></P
></LI
><LI
><P
><A
HREF="parameter-substitution.html#PATTMATCHING"
>Example 10-10</A
></P
></LI
><LI
><P
><A
HREF="parameter-substitution.html#RFE"
>Example 10-11</A
></P
></LI
><LI
><P
><A
HREF="parameter-substitution.html#VARMATCH"
>Example 10-13</A
></P
></LI
><LI
><P
><A
HREF="contributed-scripts.html#INSERTIONSORT"
>Example A-36</A
></P
></LI
><LI
><P
><A
HREF="contributed-scripts.html#QKY"
>Example A-41</A
></P
></LI
></OL
>
      </P
></DIV
></DIV
><H3
CLASS="FOOTNOTES"
>Notes</H3
><TABLE
BORDER="0"
CLASS="FOOTNOTES"
WIDTH="100%"
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN5987"
HREF="string-manipulation.html#AEN5987"
><SPAN
CLASS="footnote"
>[1]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>This applies to either command-line
	       arguments or parameters passed to a <A
HREF="functions.html#FUNCTIONREF"
>function</A
>.</P
></TD
></TR
><TR
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="5%"
><A
NAME="FTN.AEN6164"
HREF="string-manipulation.html#AEN6164"
><SPAN
CLASS="footnote"
>[2]</SPAN
></A
></TD
><TD
ALIGN="LEFT"
VALIGN="TOP"
WIDTH="95%"
><P
>Note that
	      <TT
CLASS="REPLACEABLE"
><I
>$substring</I
></TT
> and
	      <TT
CLASS="REPLACEABLE"
><I
>$replacement</I
></TT
> may refer to
	      either <I
CLASS="FIRSTTERM"
>literal strings</I
> or
	      <I
CLASS="FIRSTTERM"
>variables</I
>, depending on
	      context. See the first usage example.</P
></TD
></TR
></TABLE
><DIV
CLASS="NAVFOOTER"
><HR
ALIGN="LEFT"
WIDTH="100%"><TABLE
SUMMARY="Footer navigation table"
WIDTH="100%"
BORDER="0"
CELLPADDING="0"
CELLSPACING="0"
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
><A
HREF="manipulatingvars.html"
ACCESSKEY="P"
>Prev</A
></TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="index.html"
ACCESSKEY="H"
>Home</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
><A
HREF="parameter-substitution.html"
ACCESSKEY="N"
>Next</A
></TD
></TR
><TR
><TD
WIDTH="33%"
ALIGN="left"
VALIGN="top"
>Manipulating Variables</TD
><TD
WIDTH="34%"
ALIGN="center"
VALIGN="top"
><A
HREF="manipulatingvars.html"
ACCESSKEY="U"
>Up</A
></TD
><TD
WIDTH="33%"
ALIGN="right"
VALIGN="top"
>Parameter Substitution</TD
></TR
></TABLE
></DIV
></BODY
></HTML
>